/*
 * File : flash_mem.c
 *
 */

#include <stdlib.h> 
#include <stdio.h>
#include "config.h"
#include "memory.h"
#include "flash_mem.h"
#include "spi_flash.h"
#include "xstring.h"
#include "tool.h"
#include "console.h"


/** Packet Format
 *   -------------------------------------------------------------------------
 *   | Valid | Total length | Total PN | PN    | length  | Data  | Check sum |
 *   -------------------------------------------------------------------------
 *   | 1byte | 2Byte        |  1byte   | 1byte |  1 byte | nbyte |  1byte    |
 *   -------------------------------------------------------------------------
 *   
 *   valid			: 0x55 (valid) 0xAA (invalid)
 *   Total lengtn	: Total length of Data
 *   Total PN		: Total number of Packet
 *   PN				: Current number of Packet
 *   Length			: Data length of Packet
 *   check sum		: data check sum (XOR Data 1st ~ length)
 */
//#define DATA_SIZE				84

/*
typedef struct _LOG_PACKET {
	unsigned char	valid;
	unsigned short	total_len;
	unsigned char	total_pn;
	unsigned char	pn;
	unsigned char	len;
	char			data[DATA_SIZE];
	unsigned char	check_sum;
}LOG_PACKET;
*/

/*
#define PACKET_SIZE				sizeof(LOG_PACKET)

#define HEADBUFSIZE				6
#define VALID					0x55
#define INVALID					0xAA
#define EMPTY					0xFF
*/

/** Flash Qbuffer status flow
 *
 *                       |--> TOHOV --|
 * |--> EMPTY --> TOHI --|            |--> TIHO --> TOHOI --|
 * |                     |--> TIHI ---|                     |
 * |                                                        |
 * |--------------------- erase flash ----------------------|
 *
 */

/*
#define FLASH_EMPTY				0xFF	///< Head outside, tail outside (empty sub-sector)
#define FLASH_TIHI				0xF0	///< Head inside, tail inside	
#define FLASH_TIHO				0xC0	///< Head outside, tail inside
#define FLASH_TOHI				0xFA	///< Head inside, tail outside
#define FLASH_TOHOV				0xF8	///< Head outside, tail outside (All valid data in sub-sector)
#define FLASH_TOHOI				0x00	///< Head outside, tail outside (All invalid data in sub-sector)
*/

/**
 * @struct	_Flash_QBuf
 * @brief	Information of Flash Qbuffer
 */
/*
typedef struct _Flash_QBuf {
	unsigned long start;		///< Flash Qbuffer Start Address
	unsigned long end;			///< Flash Qbuffer End Address
	unsigned long head;			///< Flash Qbuffer Head pointer
	unsigned long tail;			///< Flash Qbuffer Tail pointer
	unsigned short num;			///< Flash Qbuffer Packet Number
}Flash_QBuf;
*/

/*
static Flash_QBuf gLog_FQBuf;
static Flash_QBuf gPriority_GPRS_FQBuf;
static Flash_QBuf gPriority_SMS_FQBuf;
static Flash_QBuf gGPRS_LOG_FQBuf;
static Flash_QBuf gSMS_LOG_FQBuf;
*/

/* Get Flash Qbuffer information structure */
/*
static Flash_QBuf *getFQBuf(unsigned char id)
{
	Flash_QBuf *pBuf;

	switch (id)
	{
	case LOG_FQBUF:
		pBuf = &gLog_FQBuf;
		break;
	case PRIORITY_GPRS_FQBUF:
		pBuf = &gPriority_GPRS_FQBuf;
		break;
	case PRIORITY_SMS_FQBUF:
		pBuf = &gPriority_SMS_FQBuf;
		break;
	case GPRS_LOG_FQBUF:
		pBuf = &gGPRS_LOG_FQBuf;
		break;
	case SMS_LOG_FQBUF:
		pBuf = &gSMS_LOG_FQBuf;
		break;
	}
	return pBuf;
}
*/

/* Format Flash Qbuffer */
/*
void FQBuf_Init(unsigned char id)
{
	Flash_QBuf *pBuf;
	unsigned char c;
	unsigned long start;
	
	// Reset Information
	pBuf			= getFQBuf(id);
	pBuf->head		= pBuf->start + 1;
	pBuf->tail		= pBuf->start + 1;
	pBuf->num		= 0;
	
	// Erase Flash
	Console_Write("Erase Flash.", 12);
	for (start = pBuf->start; start < pBuf->end; start += SSEC_SIZE)
	{
		SPI_Flash_SubSector_Erase(start);
		Console_Write(".", 1);
		IWDG_Clear_Task();
	}
	Console_Write("End\r\n", 5);
	
	// Record Flash Qbuffer Status
	c = FLASH_TIHI;
	SPI_Flash_Fast_Write(pBuf->start, &c, 1);
}
*/

/* Calculate total packet number in Flash Qbuffer */
/*
static void UpdateQueueNum(unsigned char id)
{
	unsigned short sec_log_num;
	unsigned short head_sec;
	unsigned short tail_sec;
	unsigned short num;
	Flash_QBuf *pBuf;

	pBuf = getFQBuf(id);
	
	head_sec = pBuf->head >> 12;
	tail_sec = pBuf->tail >> 12;
	sec_log_num = 4095 / PACKET_SIZE;	//Get packet number in one sub sector
	if (head_sec == tail_sec)
	{
		// head & tail in the same sub sector
		num = (pBuf->head - pBuf->tail) / PACKET_SIZE;
	}
	else
	{
		// head & tail not in the sma sub sector
		if (head_sec < tail_sec)
		{
			head_sec += ((pBuf->end + 1 - pBuf->start) >> 12);
		}
		num = (head_sec - tail_sec - 1) * sec_log_num;
		num = num + (((pBuf->head & 0x00000FFF) - 1) / PACKET_SIZE);
		num = num + sec_log_num - (((pBuf->tail & 0x00000FFF)-1) / PACKET_SIZE);
	}
	pBuf->num = num;
}
*/

/* Search Head Tail in Flash Qbuffer */
/*
static unsigned char GetHeadTail(unsigned char id)
{
  	unsigned long start, end;
	unsigned char status, ret;
	unsigned char valid;
	Flash_QBuf *pBuf;

	pBuf	= getFQBuf(id);
	ret		= 0;
	
	for (start = pBuf->start; start < pBuf->end; start += SSEC_SIZE)
	{
		status = 0;
		SPI_Flash_Read(start, &status, 1);
		if (SPI_Flash_Wait_Ready() != SPI_FLASH_OK)
			return ret;	//Flash Read Fail
		end = start + SSEC_SIZE;
		if (status == FLASH_TIHO)
		{
			// Tail Inside //
			for (pBuf->tail = start + 1; pBuf->tail < end; pBuf->tail += PACKET_SIZE)
			{
				valid = 0;
				SPI_Flash_Read(pBuf->tail, &valid, 1);
				if (SPI_Flash_Wait_Ready() != SPI_FLASH_OK)
					return ret;	//Flash Read Fail
				if (valid == VALID)
				{
					// get tail //
					ret |= 0x50;
					break;
				}
			}
		}
		else if (status == FLASH_TOHI)
		{
			// Head Inside //
			for (pBuf->head = start + 1; pBuf->head < end; pBuf->head += PACKET_SIZE)
			{
				valid = 0;
				SPI_Flash_Read(pBuf->head, &valid, 1);
				if (SPI_Flash_Wait_Ready() != SPI_FLASH_OK)
					return ret; //Flash Read Fail
				if (valid == EMPTY)
				{
					// get head //
					ret |= 0x0A;
					break;
				}
			}
		}
		else if (status == FLASH_TIHI)
		{
			// Head and Tail Inside //
			for (pBuf->head = start + 1; pBuf->head < end; pBuf->head += PACKET_SIZE)
			{
				valid = 0;
				SPI_Flash_Read(pBuf->head, &valid, 1);
				if (SPI_Flash_Wait_Ready() != SPI_FLASH_OK)
					return ret; //Flash Read Fail
				if ((valid == VALID) && ((ret & 0xF0) != 0x50))
				{
					// get tail //
					pBuf->tail = pBuf->head;
					ret |= 0x50;
				}
				if (valid == EMPTY)
				{
					// get head //
					if (ret != 0x0A)
					{
						ret |= 0x0A;
						if (ret == 0x0A)
						{
							pBuf->tail = pBuf->head;
							ret |= 0x50;
						}
						return ret;
					}	
				}
				if (ret == 0x5A)
					return ret;	//Get Head & Tail
			}
		}
		if (ret == 0x5A)
			return ret;	//Get Head & Tail
	}
	return ret;
    return 0;
}
*/

/* Get Flash Qbuffer Status */
/*
static void Check_Flash_Status(unsigned long addr, unsigned char status)
{

	unsigned char temp = 0;
	
	addr &= 0x00FFF000;
	SPI_Flash_Read(addr, &temp, 1);
	SPI_Flash_Wait_Ready();
	if (temp != status)
		*pFlag_err_flash_rw = 1;

}
*/

/* Adjust Head Point if change sub sector */
/*
static void AdjustHeadPoint(unsigned char id)
{
	unsigned long addr;
	unsigned char status;
	Flash_QBuf *pBuf;
	
	pBuf	= getFQBuf(id);
	addr	= (pBuf->head & 0x00FFF000);
	status	= 0;
	SPI_Flash_Read(addr, &status, 1);
	if (SPI_Flash_Wait_Ready() != SPI_FLASH_OK)
		return;
	if (status == FLASH_TOHI)
	{
		// TOHI -> TOHOV
		status = FLASH_TOHOV;
		SPI_Flash_Fast_Write(addr, &status, 1);
		Check_Flash_Status(addr, status);
	}
	else if (status == FLASH_TIHI)
	{
		// TIHI -> TIHO
		status = FLASH_TIHO;
		SPI_Flash_Fast_Write(addr, &status, 1);
		Check_Flash_Status(addr, status);
	}
	else if ((status == FLASH_EMPTY) || (status == FLASH_TOHOI))
	{
		// TOHOI -> EMPTY -> TOHI
		SPI_Flash_SubSector_Erase(addr);
		SPI_Flash_Wait_Ready();
		status = FLASH_TOHI;
		SPI_Flash_Fast_Write(addr, &status, 1);
		Check_Flash_Status(addr, status);
	}
	else if (status == FLASH_TIHO)
	{
		// TIHO -> TOHI
		if (addr == (pBuf->tail & 0x00FFF000))
		{
			if ((addr + SSEC_SIZE) > pBuf->end)
			{
				// shift Tail to next subsector
				SPI_Flash_Fast_Write(pBuf->start, &status, 1);
				Check_Flash_Status(pBuf->start, status);
				pBuf->tail = pBuf->start + 1;
			}
			else
			{
				// shift Tail to next subsector 
				SPI_Flash_Fast_Write((addr + SSEC_SIZE), &status, 1);
				Check_Flash_Status((addr + SSEC_SIZE), status);
				pBuf->tail = addr + SSEC_SIZE + 1;
			}
		}
		// full, need erase one sub sector
		SPI_Flash_SubSector_Erase(addr);
		SPI_Flash_Wait_Ready();
		status = FLASH_TOHI;
		SPI_Flash_Fast_Write(addr, &status, 1);
		Check_Flash_Status(addr, status);
		// update packet number in flash queue
		UpdateQueueNum(id);
	}
	else
	{
		*pFlag_err_flash_rw = 1;
	}
}
*/

/* Adjust Tail Point */
/*
static void AdjustTailPoint(unsigned char id)
{
	unsigned long addr;
	unsigned char status;
	Flash_QBuf *pBuf;
	
	pBuf	= getFQBuf(id);
	addr	= (pBuf->tail & 0x00FFF000);
	status	= 0;
	SPI_Flash_Read(addr, &status, 1);
	if (SPI_Flash_Wait_Ready() != SPI_FLASH_OK)
		return;
	if (status == FLASH_TIHO)
	{
		// TIHO -> TOHOI
		status = FLASH_TOHOI;
		SPI_Flash_Fast_Write(addr, &status, 1);
		Check_Flash_Status(addr, status);
	}
	else if (status == FLASH_TIHI)
	{
		// TIHI -> TOHI //
		status = FLASH_TOHI;
		SPI_Flash_Fast_Write(addr, &status, 1);
		Check_Flash_Status(addr, status);
	}
	else if ((status == FLASH_EMPTY) || (status == FLASH_TOHOV))
	{
		// TOHOV -> TIHO //
		status = FLASH_TIHO;
		SPI_Flash_Fast_Write(addr, &status, 1);
		Check_Flash_Status(addr, status);
	}
	else if (status == FLASH_TOHI)
	{
		// TOHI -> TIHI //
		status = FLASH_TIHI;
		SPI_Flash_Fast_Write(addr, &status, 1);
		Check_Flash_Status(addr, status);
	}
	else
	{
		*pFlag_err_flash_rw = 1;
	}
}
*/

/* Add data in flash queue */
/*
void Flash_Queue_Add(unsigned char id, char *buf, unsigned short len)
{
	unsigned char i;
	unsigned char temp_len;
	unsigned short remain_len;
	Flash_QBuf *pBuf;
	LOG_PACKET *packet_buf;
	
	pBuf			= getFQBuf(id);
	packet_buf		= (LOG_PACKET *)malloc(sizeof(LOG_PACKET));
	assert_malloc(packet_buf);

	packet_buf->valid		= VALID;
	packet_buf->total_len	= len;
	// calculate total packet number
	if ((len % DATA_SIZE) == 0)
		packet_buf->total_pn = len / DATA_SIZE;
	else
		packet_buf->total_pn = (len / DATA_SIZE) + 1 ;
	

	for (i = 1; len > 0; i++)
	{
		remain_len = (unsigned short)(0x1000 - (pBuf->head & 0x00000FFF));
		xmemset((void *)&packet_buf->data, 0, DATA_SIZE);
		if (len > DATA_SIZE)
		{
			temp_len = DATA_SIZE;
			len -= DATA_SIZE;
		}
		else
		{
			temp_len = len;
			len = 0;
		}
		if (remain_len < PACKET_SIZE)
		{
			// not enough space to add new packet, so change next sub sector
			AdjustHeadPoint(id);	//update current sector status
			pBuf->head += remain_len;
			if (pBuf->head > pBuf->end)
				pBuf->head = pBuf->start + 1;
			else
				pBuf->head++;
			AdjustHeadPoint(id);	//update next sector status
		}
		packet_buf->pn	= i;
		packet_buf->len	= temp_len;
		xmemcpy((void *)&packet_buf->data, (void *)buf, temp_len);
		buf += temp_len;
		packet_buf->check_sum = Calc_CheckSum((char *)&packet_buf->data, temp_len);
		SPI_Flash_Fast_Write(pBuf->head, (unsigned char *)packet_buf, PACKET_SIZE);
		pBuf->num++;
		if (remain_len == PACKET_SIZE)
		{
			// current sub sector is full, change next sub sector
			AdjustHeadPoint(id);	//update current sector status
			pBuf->head += PACKET_SIZE;
			if (pBuf->head > pBuf->end)
				pBuf->head = pBuf->start + 1;
			else
				pBuf->head++;
			AdjustHeadPoint(id);	//update next sector status
		}
		else
		{
			pBuf->head += PACKET_SIZE;
		}
		
	}
	free(packet_buf);
	assert_free();
}
*/

/* delete data in flash queue */
/*
void Flash_Queue_Del(unsigned char id)
{
	unsigned char valid;
	unsigned short remain_len;
	Flash_QBuf *pBuf;
	LOG_PACKET *packet_buf;
	
	packet_buf		= (LOG_PACKET *)malloc(sizeof(LOG_PACKET));
	assert_malloc(packet_buf);
	pBuf = getFQBuf(id);
DEL:
	if (pBuf->tail != pBuf->head)
	{
		remain_len = (unsigned short)(0x1000 - (pBuf->tail & 0x00000FFF));
		if (remain_len < PACKET_SIZE)
		{
			// real tail in next subsector 
			AdjustTailPoint(id);	//update current sector status
			pBuf->tail += remain_len;
			if (pBuf->tail > pBuf->end)
				pBuf->tail = pBuf->start + 1;
			else
				pBuf->tail += 1;
			AdjustTailPoint(id);	//update next sector status
		}
		if (pBuf->tail == pBuf->head)
		{
			// no data to delete
			goto DEL_OUT;
		}
		// get header
		SPI_Flash_Read(pBuf->tail, (unsigned char *)packet_buf, HEADBUFSIZE);
		// mark the data as invaild
		valid = INVALID;
		SPI_Flash_Write(pBuf->tail, &valid, 1);
		if (remain_len == PACKET_SIZE)
		{
			// current sub sector is empty, change next sub sector
			AdjustTailPoint(id);	//update current sector status
			pBuf->tail += PACKET_SIZE;
			if (pBuf->tail > pBuf->end)
				pBuf->tail = pBuf->start + 1;
			else
				pBuf->tail += 1;
			AdjustTailPoint(id);	//update next sector status
		}
		else
		{
			pBuf->tail += PACKET_SIZE;
		}
		
		pBuf->num--;
		if (packet_buf->total_pn > packet_buf->pn)
			goto DEL;
	}
DEL_OUT:
	free(packet_buf);
	assert_free();
}
*/

/* Read Flash Data */
/*
unsigned short Flash_Queue_Read(unsigned char id, char *buf, unsigned short num, unsigned short buf_len)
{
	unsigned short sector_log_num;
	unsigned short current_log_num;
	unsigned short remain_log_num;
	unsigned short len;
	unsigned long addr;
	Flash_QBuf *pBuf;
	LOG_PACKET *packet_buf;
	
	pBuf = getFQBuf(id);
	addr = pBuf->tail;
	sector_log_num = 4095 / PACKET_SIZE;
	
	if (num > pBuf->num)
	{
		return 0;
	}
	while (num > 0)
	{
		// get the n packet address
		current_log_num = ((addr & 0x00000FFF) - 1) / PACKET_SIZE;
		remain_log_num = sector_log_num - current_log_num;
		if (num > remain_log_num)
		{
			addr = (addr & 0x00FFF000) + 0x1000;
			if (addr > pBuf->end)
				addr = pBuf->start;
			addr += 1;
			num -= remain_log_num;
		}
		else
		{
			addr = addr + (num - 1) * PACKET_SIZE;
			num = 0;
		}
	}
	
	len = 0;
	packet_buf = (LOG_PACKET *)malloc(sizeof(LOG_PACKET));
	assert_malloc(packet_buf);
READ:
	SPI_Flash_Read(addr, (unsigned char *)packet_buf, PACKET_SIZE);
	SPI_Flash_Wait_Ready();
	if (packet_buf->check_sum != Calc_CheckSum((char *)&packet_buf->data, packet_buf->len))
	{
		len = 0;
		goto READ_OUT;
	}
	if (packet_buf->total_len > buf_len)
	{
		len = 0;
		goto READ_OUT;
	}
	xmemcpy(buf, packet_buf->data, packet_buf->len);
	buf += packet_buf->len;
	len += packet_buf->len;
	if (packet_buf->total_pn > packet_buf->pn)
	{
		addr += PACKET_SIZE;
		if ((addr & 0x00000FFF) == 0)
		{
			if (addr > pBuf->end)
				addr = pBuf->start;
			addr += 1;
		}
		else
		{
			if ((0x1000 - (addr & 0x00FFF000)) < PACKET_SIZE)
			{
				addr = (addr & 0x00FFF000) + 0x1000;
				if (addr > pBuf->end)
					addr = pBuf->start;
				addr += 1;
			}
		}
		goto READ;
	}
READ_OUT:
	free(packet_buf);
	assert_free();
	return len;
  return 0;
}
*/

/* get packet num in flash queue */
/*
unsigned short Flash_Queue_Get_num(unsigned char id)
{
	Flash_QBuf *pBuf;
	
	pBuf = getFQBuf(id);
	return pBuf->num;
}
*/

/* Initail flash qbuffer information */
/*
void Flash_Mem_Init(void)
{
	int i;
	unsigned char ret;
	
	gLog_FQBuf.start			= LOG_ADDR;
	gLog_FQBuf.end				= LOG_ADDR + LOG_SIZE * 4 * 1024 - 1;
	gPriority_GPRS_FQBuf.start	= PRIORITY_GPRS_ADDR;
	gPriority_GPRS_FQBuf.end	= PRIORITY_GPRS_ADDR + PRIORITY_GPRS_SIZE * 4 * 1024 - 1;
	gPriority_SMS_FQBuf.start	= PRIORITY_SMS_ADDR;
	gPriority_SMS_FQBuf.end		= PRIORITY_SMS_ADDR + PRIORITY_SMS_SIZE * 4 * 1024 - 1;
	gGPRS_LOG_FQBuf.start		= GPRS_LOG_ADDR;
	gGPRS_LOG_FQBuf.end			= GPRS_LOG_ADDR + GPRS_LOG_SIZE * 4 * 1024 - 1;
	gSMS_LOG_FQBuf.start		= SMS_LOG_ADDR;
	gSMS_LOG_FQBuf.end			= SMS_LOG_ADDR + SMS_LOG_SIZE * 4 * 1024 - 1;
	
	for (i = 0; i < 5; i++)
	{
		ret = GetHeadTail(i);
		if (ret != 0x5A)
		{
			FQBuf_Init(i);
		}
		UpdateQueueNum(i);
	}
}
*/
